﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Markup;
using System.Text;
using System.Collections.Generic;
using System.Windows.Media.Imaging;
using System.Windows.Browser;
using System.IO;
using System.Collections.ObjectModel;

namespace OnlineBattlemat
{
	public partial class Page : UserControl
    {
        //by 4/28/08
        //TODO: x-Fix buttons/bugs from proto comments
        //      x-Erasing lines + tokens...
        //      x-Actually use uploads, hopefully? you betcha
        //      x-Something with spell templates of some sort.
        //      x-Fix the token-dragging bug (snaps token corner to mouse cursor)
        //      x?-A little zooming --Scaling accurately - still scrollbar positioning bugs? - some strange dragable area bugs
        //      x-Show feedback for currently selected tool
        //      x-Split up Pointer and Eraser?
        //      ~-Add a text label tool? --KL: Probably not - hard to implement and not worth it just for the deliverable.
        //      x-CURSORS!!!!!! to some extent.
        //		x-Different sizes of icons
        //		-Shows uploaded tokens in the list

        public enum Tools { Pointer, Pen, Eraser, Template, Token, Text };

		#region Private Fields
		//The current page instance
		private static Page m_CurrentInstance;

		//The stroke that is currently being drawn
		private Stroke m_NewStroke = null;

		//The color currently being drawn with
		private Color m_CurrentInkColor = Colors.Black;

		//The currently displayed dialog
		private UserControl m_CurrentDialog = null;

		//The original point when the user starts dragging
		private Point m_OriginalPoint = new Point(-1, -1);

		//Used only the first time this loads for a janky fix
		private bool m_IsLoading = true;

		//List of Tokens in the system
		private ObservableCollection<Token> m_Tokens;

		//Current user name
		private string m_UserName = "[User]";

		//Current session name
		private string m_SessionName = "[Session Name]";

		//Whether or not the battlemat is being dragged
		private bool m_IsDraggingCanvas = false;

        //Whether or not the eraser tools is being dragged around
        private bool m_IsDraggingEraser = false;

        //The amount to zoom in/out (0.5 == +-50%)
        private const double m_ZoomScale = 0.25;

		private Tools m_CurrentTool;
		#endregion

        #region Properties
        /// <summary>
        /// The name of the current user
        /// </summary>
        public String UserName
        {
            get
            {
                return m_UserName;
            }
            set
            {
                m_UserName = value;
                x_CurrentUserTextBlock.Text = "Welcome, " + value;
            }
        }

        /// <summary>
        /// The name of the current session
        /// </summary>
        public String SessionName
        {
            get
            {
                return m_SessionName;
            }
            set
            {
                m_SessionName = value;
                x_SessionNameTextBlock.Text = value;
            }
        }

        /// <summary>
        /// Tokens that are currently loaded into the system
        /// </summary>
        public ObservableCollection<Token> Tokens
        {
            get { return m_Tokens; }
            set { m_Tokens = value; }
        }

        public Tools CurrentTool
        {
            get
            {
                return m_CurrentTool;
            }
            set
            {
                m_CurrentTool = value;

                //Set feedback and things
                switch (value)
                {
                    case Tools.Pointer:
                        x_MainCanvas.Cursor = Cursors.Hand;
						x_SelectedToolImage.Source =  new BitmapImage(new Uri("images/select2.png", UriKind.Relative));
                        break;

                    case Tools.Pen:
                        x_MainCanvas.Cursor = Cursors.Stylus;
                        if (m_CurrentInkColor == Colors.Black)
                        {
							x_SelectedToolImage.Source = new BitmapImage(new Uri("images/blackPen.png", UriKind.Relative));
                        }
                        else if (m_CurrentInkColor == Colors.Blue)
                        {
							x_SelectedToolImage.Source = new BitmapImage(new Uri("images/bluePen.png", UriKind.Relative));
                        }
                        else if (m_CurrentInkColor == Colors.Green)
                        {
							x_SelectedToolImage.Source = new BitmapImage(new Uri("images/greenPen.png", UriKind.Relative));
                        }
                        else if (m_CurrentInkColor == Colors.Red)
                        {
							x_SelectedToolImage.Source = new BitmapImage(new Uri("images/redPen.png", UriKind.Relative));
                        }
                        break;

                    case Tools.Eraser:
                        x_MainCanvas.Cursor = Cursors.Eraser;
						x_SelectedToolImage.Source = new BitmapImage(new Uri("images/eraser.png", UriKind.Relative));
                        break;

                    case Tools.Template:
                        CurrentTool = Tools.Pointer;
                        break;

                    case Tools.Token:
                        CurrentTool = Tools.Pointer;
                        break;

                    case Tools.Text:
                        x_MainCanvas.Cursor = Cursors.IBeam;
                        break;

                    default:
                        CurrentTool = Tools.Pointer;
                        break;
                }
            }
        }
        #endregion

        #region Constructors
        static Page()
        {
        }

        public Page()
        {
            m_CurrentInstance = this;

            // Required to initialize variables
            InitializeComponent();

            //jordan - Added to handle resizing the page to keep the battlemat at full size
            this.SizeChanged += new SizeChangedEventHandler(Page_SizeChanged);
            this.Loaded += new RoutedEventHandler(Page_Loaded);

            m_Tokens = new ObservableCollection<Token>();
            loadDefaultTokens();
            x_TokenListBox.ItemsSource = m_Tokens;

            //Show the join dialog initially
            ShowDialog(x_JoinSessionDialog);
        }

		#endregion

        #region Event Handlers

        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            //Draw the grid lines
            DrawGridLines();
            CurrentTool = Tools.Pointer;

            //Add scrollwheel support for zooming
            HtmlPage.Window.AttachEvent("DOMMouseScroll", OnMouseWheelTurned);
            HtmlPage.Window.AttachEvent("onmousewheel", OnMouseWheelTurned);
            HtmlPage.Document.AttachEvent("onmousewheel", OnMouseWheelTurned);

        }

		//jordan - Added to handle resizing the page to keep the battlemat at full size
		private void Page_SizeChanged(object sender, SizeChangedEventArgs e)
		{
			//Height of the page minus the height of the header row
			x_MainScrollViewer.Height = this.ActualHeight - 45;
			x_MainScrollViewer.Width = this.ActualWidth;

			if (m_IsLoading)
			{
				MoveToCenter();
			}
        }

        private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            closeAllPopups();
        }

        private void OnMouseWheelTurned(Object sender, HtmlEventArgs args)
        {
            double delta = 0;
            ScriptObject e = args.EventObject;

            //Only zoom if Ctrl is being held
            if ((Keyboard.Modifiers & ModifierKeys.Control) != 0)
            {

                if (e.GetProperty("wheelDelta") != null) // IE and Opera
                {
                    delta = ((double)e.GetProperty("wheelDelta"));
                    if (HtmlPage.Window.GetProperty("opera") != null)
                        delta = -delta;
                }
                else if (e.GetProperty("detail") != null) // Mozilla and Safari
                {
                    delta = -((double)e.GetProperty("detail"));
                }

                if (delta > 0)
                {
                    // Zoom in
                    x_CanvasZoomTransform.ScaleX += 0.1;
                    x_CanvasZoomTransform.ScaleY += 0.1;
                }
                else if (delta < 0)
                {
                    // Zoom out
                    x_CanvasZoomTransform.ScaleX -= 0.1;
                    x_CanvasZoomTransform.ScaleY -= 0.1;
                }

                if (delta != 0)
                {
                    args.PreventDefault();
                    e.SetProperty("returnValue", false);
                }
            }
        }


        #region Inking and Grid Movement Support
        private void x_InkPresenterElement_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (CurrentTool == Tools.Pen)
            {
                x_InkPresenterElement.CaptureMouse();
                m_NewStroke = new Stroke();
                m_NewStroke.StylusPoints.Add(e.StylusDevice.GetStylusPoints(x_InkPresenterElement));
                m_NewStroke.DrawingAttributes.Color = m_CurrentInkColor;
                x_InkPresenterElement.Strokes.Add(m_NewStroke);
            }
            else if (CurrentTool == Tools.Pointer)
            {
                x_InkPresenterElement.CaptureMouse();
                m_IsDraggingCanvas = true;
                m_OriginalPoint = e.GetPosition(null);
            }
            else if (CurrentTool == Tools.Eraser)
            {
                x_InkPresenterElement.CaptureMouse();
                m_IsDraggingEraser = true;

                Stroke eraseTarget = null;
                foreach (Stroke s in x_InkPresenterElement.Strokes)
                {
                    if (s.HitTest(e.StylusDevice.GetStylusPoints(x_InkPresenterElement)))
                    {
                        eraseTarget = s;
                    }
                }

                if (eraseTarget != null)
                {
                    x_InkPresenterElement.Strokes.Remove(eraseTarget);
                }
            }
        }

        private void x_InkPresenterElement_MouseMove(object sender, MouseEventArgs e)
        {
            if (CurrentTool == Tools.Pen)
            {
                if (m_NewStroke != null)
                {
                    m_NewStroke.StylusPoints.Add(e.StylusDevice.GetStylusPoints(x_InkPresenterElement));
                }
            }
            else if (CurrentTool == Tools.Pointer)
            {
                if (m_IsDraggingCanvas && m_OriginalPoint.X != -1 && m_OriginalPoint.Y != -1)
                {
                    Point newPoint = e.GetPosition(null);

                    x_MainScrollViewer.ScrollToHorizontalOffset(x_MainScrollViewer.HorizontalOffset + (m_OriginalPoint.X - newPoint.X));
                    x_MainScrollViewer.ScrollToVerticalOffset(x_MainScrollViewer.VerticalOffset + (m_OriginalPoint.Y - newPoint.Y));
                    m_OriginalPoint = newPoint;
                }
            }
            else if (CurrentTool == Tools.Eraser && m_IsDraggingEraser)
            {
                Stroke eraseTarget = null;
                foreach (Stroke s in x_InkPresenterElement.Strokes)
                {
                    if (s.HitTest(e.StylusDevice.GetStylusPoints(x_InkPresenterElement)))
                    {
                        eraseTarget = s;
                    }
                }

                if (eraseTarget != null)
                {
                    x_InkPresenterElement.Strokes.Remove(eraseTarget);
                }
            }
        }

        private void x_InkPresenterElement_MouseUp(object sender, MouseButtonEventArgs e)
        {
            if (m_NewStroke != null)
            {
                m_NewStroke = null;
            }


            if (m_IsDraggingCanvas && m_OriginalPoint.X != -1 && m_OriginalPoint.Y != -1)
            {
                //I'm confused what all this OriginalPoint stuff here is supposed to do but I left it in. ~KL 3/24/10
                m_OriginalPoint = new Point(-1, -1);
                x_InkPresenterElement.ReleaseMouseCapture();
                m_IsDraggingCanvas = false;
            }

            if (m_IsDraggingEraser)
            {
                x_InkPresenterElement.ReleaseMouseCapture();
                m_IsDraggingEraser = false;
            }
        }

        private void x_InkPresenterElement_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Up:
                    x_MainScrollViewer.ScrollToVerticalOffset(x_MainScrollViewer.VerticalOffset + 5);
                    break;
                case Key.Down:
                    x_MainScrollViewer.ScrollToVerticalOffset(x_MainScrollViewer.VerticalOffset - 5);
                    break;
                case Key.Left:
                    x_MainScrollViewer.ScrollToHorizontalOffset(x_MainScrollViewer.HorizontalOffset - 5);
                    break;
                case Key.Right:
                    x_MainScrollViewer.ScrollToHorizontalOffset(x_MainScrollViewer.HorizontalOffset + 5);
                    break;
                default:
                    break;
            }
        }
        #endregion

        #region Zooming Support
        private void x_ZoomInButton_Click(object sender, RoutedEventArgs e)
        {
            x_CanvasZoomTransform.ScaleX += m_ZoomScale;
            x_CanvasZoomTransform.ScaleY += m_ZoomScale;

            x_MainScrollViewer.ScrollToHorizontalOffset(x_MainScrollViewer.HorizontalOffset * x_CanvasZoomTransform.ScaleX);
            x_MainScrollViewer.ScrollToVerticalOffset(x_MainScrollViewer.VerticalOffset * x_CanvasZoomTransform.ScaleY);
        }

        private void x_ZoomCenterButton_Click(object sender, RoutedEventArgs e)
        {
            //x_CanvasZoomTransform.ScaleX = 1;
            //x_CanvasZoomTransform.ScaleY = 1;

            MoveToCenter();
        }

        private void x_ZoomOutButton_Click(object sender, RoutedEventArgs e)
        {
            //jordan - only zoom out more if it won't go negative
            if (x_CanvasZoomTransform.ScaleX - m_ZoomScale > 0)
            {
                x_CanvasZoomTransform.ScaleX -= m_ZoomScale;
                x_CanvasZoomTransform.ScaleY -= m_ZoomScale;

                x_MainScrollViewer.ScrollToHorizontalOffset(x_MainScrollViewer.HorizontalOffset * x_CanvasZoomTransform.ScaleX);
                x_MainScrollViewer.ScrollToVerticalOffset(x_MainScrollViewer.VerticalOffset * x_CanvasZoomTransform.ScaleY);
            }
        }

		private void x_FullscreenButton_Click(object sender, RoutedEventArgs e)
		{
			Application.Current.Host.Content.IsFullScreen = !Application.Current.Host.Content.IsFullScreen;
		}
        #endregion

        #region Buttons Support
        private void ColorPickerButton_Click(object sender, RoutedEventArgs e)
        {
            if ((sender as Button) == x_ColorBlackButton)
            {
                m_CurrentInkColor = Colors.Black;
				x_ColorButtonImage.Source = new BitmapImage(new Uri("images/blackPen_button.png", UriKind.Relative));
            }
            else if ((sender as Button) == x_ColorBlueButton)
            {
                m_CurrentInkColor = Colors.Blue;
				x_ColorButtonImage.Source = new BitmapImage(new Uri("images/bluePen_button.png", UriKind.Relative));
            }
            else if ((sender as Button) == x_ColorGreenButton)
            {
                m_CurrentInkColor = Colors.Green;
				x_ColorButtonImage.Source = new BitmapImage(new Uri("images/greenPen_button.png", UriKind.Relative));
            }
            else if ((sender as Button) == x_ColorRedButton)
            {
                m_CurrentInkColor = Colors.Red;
				x_ColorButtonImage.Source = new BitmapImage(new Uri("images/redPen_button.png", UriKind.Relative));
            }

            CurrentTool = Tools.Pen;
            x_ColorPopup.IsOpen = false;

        }

		private void TemplatePickerButton_Click(object sender, RoutedEventArgs e)
        {
            if ((sender as Button) == x_10ftRadiusTemplateButton)
            {
                Token spellTemplate = new Token("[10-ft. Radius]", "images/tmplt_10ftRadius.png");
                AddSpellTemplateToPage(spellTemplate);
            }
            else if ((sender as Button) == x_20ftRadiusTemplateButton)
            {
                Token spellTemplate = new Token("[20-ft. Radius]", "images/tmplt_20ftRadius.png");
                AddSpellTemplateToPage(spellTemplate);
            }

            CurrentTool = Tools.Template;
            x_TemplatesPopup.IsOpen = false;
        }

		private void TokenPickerButton_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (x_TokensPopup.IsOpen)
            {
                CurrentTool = Tools.Token;
				//BitmapImage image = (((sender as StackPanel).Children[0] as Image).Source as BitmapImage);
				//string name = ((sender as StackPanel).Children[1] as TextBlock).Text;
				Token token = (sender as StackPanel).DataContext as Token;

                AddTokenToPage(token, 1);

                x_TokensPopup.IsOpen = false;
            }
        }

        private void x_PointerButton_Click(object sender, RoutedEventArgs e)
        {
            CurrentTool = Tools.Pointer;
        }

        private void x_EraserButton_Click(object sender, RoutedEventArgs e)
        {
            CurrentTool = Tools.Eraser;
        }

        private void x_ColorButton_Click(object sender, RoutedEventArgs e)
        {
            bool oldIsOpen = x_ColorPopup.IsOpen;

            closeAllPopups();
            x_ColorPopup.IsOpen = !oldIsOpen;
        }

        private void x_TemplatesButton_Click(object sender, RoutedEventArgs e)
        {
            bool oldIsOpen = x_TemplatesPopup.IsOpen;

            closeAllPopups();
            x_TemplatesPopup.IsOpen = !oldIsOpen;
        }

        private void x_TokensButton_Click(object sender, RoutedEventArgs e)
        {
            bool oldIsOpen = x_TokensPopup.IsOpen;

            closeAllPopups();
            x_TokensPopup.IsOpen = !oldIsOpen;
        }

        private void x_UploadTokenButton_Click(object sender, RoutedEventArgs e)
        {
            closeAllPopups();
            ShowDialog(x_UploadImageDialog);
        }
		#endregion
		#endregion

		#region Private Methods
        private void DrawGridLines()
        {
            double marginLeft = 0;
            double marginRight = 0;
            double marginBottom = 0;
            double marginTop = 0;

            double width = 4000;
            double height = 4000;

            //Make it seem like an infinite grid...or at least try
            double gridWidth = (width - marginLeft - marginRight);
            double gridHeight = (height - marginTop - marginBottom);

            // Now draw the grid lines
            System.Windows.Shapes.Path pathElem = new System.Windows.Shapes.Path();
            pathElem.Stroke = new SolidColorBrush(Colors.Black);
            pathElem.StrokeThickness = .5;
            pathElem.Fill = new SolidColorBrush(Colors.Transparent);

            //100x100 squares?
            int vLineCount = 75;
            int hLineCount = 75;

            //Fix the gridlines - had to use LineGeometry objects with start/endpoints instead of special markup language.  -KL 2/24/09
            GeometryGroup gg = new GeometryGroup();

            // horizontal lines
            for (int i = 0; i < hLineCount - 1; ++i)
            {
                LineGeometry horizontalLine = new LineGeometry();
                horizontalLine.StartPoint = new Point(marginLeft, (i + 1) * gridHeight / hLineCount + marginTop);
                horizontalLine.EndPoint = new Point(horizontalLine.StartPoint.X + gridWidth, horizontalLine.StartPoint.Y);
                gg.Children.Add(horizontalLine);
            }

            // vertical lines
            for (int i = 0; i < vLineCount - 1; ++i)
            {
                LineGeometry verticalLine = new LineGeometry();
                verticalLine.StartPoint = new Point(marginLeft + ((i + 1) * gridWidth / vLineCount), marginTop);
                verticalLine.EndPoint = new Point(verticalLine.StartPoint.X, verticalLine.StartPoint.Y + gridHeight);
                gg.Children.Add(verticalLine);
            }

            pathElem.SetValue(System.Windows.Shapes.Path.DataProperty, gg);

            x_MainCanvas.Children.Add(pathElem);
        }

        private void closeAllPopups()
        {
            x_ColorPopup.IsOpen = false;
            //x_ToolsPopup.IsOpen = false;
            x_TemplatesPopup.IsOpen = false;
            x_TokensPopup.IsOpen = false;
        }

        private void loadDefaultTokens()
        {
            Token token;
            int count = 0;

            while(count < 4)
            {
                string filepath = "images/token" + count.ToString() + ".png";

                token = new Token("Token" + count.ToString(), filepath);
                Tokens.Add(token);
                count++;
            }
            
        }

		#endregion

        #region Public Methods
        /// <summary>
        /// Gets the current Page instace
        /// </summary>
        /// <returns></returns>
        public static Page GetCurrentPage()
        {
            return m_CurrentInstance;
        }

        /// <summary>
        /// Shows the given dialog with the dropcloth
        /// </summary>
        /// <param name="dialog">The dialog to show</param>
        public void ShowDialog(UserControl dialog)
        {
            if (m_CurrentDialog != null)
            {
                m_CurrentDialog.Visibility = Visibility.Collapsed;
            }

            dialog.Visibility = Visibility.Visible;
            x_DropClothRectangle.Visibility = Visibility.Visible;
            m_CurrentDialog = dialog;
        }

        /// <summary>
        /// Hides the currently displayed dialog and dropcloth
        /// </summary>
        public void HideDialogs()
        {
            if (m_CurrentDialog != null)
            {
                m_CurrentDialog.Visibility = Visibility.Collapsed;
            }
            x_DropClothRectangle.Visibility = Visibility.Collapsed;
        }

        /// <summary>
        /// Moves the current view to the center of the mat
        /// </summary>
        public void MoveToCenter()
        {
            x_MainScrollViewer.ScrollToHorizontalOffset(x_MainCanvas.ActualWidth * .5); //used to be Width...not sure changing it made a difference...
            x_MainScrollViewer.ScrollToVerticalOffset(x_MainCanvas.ActualHeight * .5); //used to be Height...
        }

        /// <summary>
        /// Adds the given token to the canvas for use by the user
        /// </summary>
        /// <param name="token">The token to show on the canvas</param>
        public void AddTokenToPage(Token token, int size)
        {
            TokenControl tc = new TokenControl(token);

            x_InkPresenterElement.Children.Add(tc);
            tc.SetValue(Canvas.LeftProperty, x_MainScrollViewer.HorizontalOffset + 100.0);
            tc.SetValue(Canvas.TopProperty, x_MainScrollViewer.VerticalOffset + 100.0);
            tc.SetValue(Canvas.ZIndexProperty, 15);

            tc.Height = 50 * size;
            tc.Width = 50 * size;
            tc.x_TokenImage.Height = 50 * size;
            tc.x_TokenImage.Width = 50 * size;
        }

        /// <summary>
        /// Adds the given spell template image to the canvas
        /// </summary>
        /// <param name="spellTemplate">The template token to add</param>
        public void AddSpellTemplateToPage(Token spellTemplate)
        {
            TokenControl tc = new TokenControl(spellTemplate);

            x_MainCanvas.Children.Add(tc);
            tc.SetValue(Canvas.LeftProperty, x_MainScrollViewer.HorizontalOffset + 100.0);
            tc.SetValue(Canvas.TopProperty, x_MainScrollViewer.VerticalOffset + 100.0);
            tc.SetValue(Canvas.ZIndexProperty, 10);

            tc.x_Border.BorderThickness = new Thickness(0);
            tc.Opacity = 0.5;
        }
		#endregion


	}
}
